home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Devices and Hardware / SCSI / SCSI Find Devices / SCSIFindNextDevice.c < prev   
Encoding:
C/C++ Source or Header  |  2000-09-28  |  31.3 KB  |  1,001 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        SCSIFindNextDevice.c
  3.  
  4.     Contains:    Find all SCSI devices. The alogrithm first asks the SCSI Manager for the
  5.                 number of buses, then loops through each bus for each device and LUN.
  6.                 old SCSI Manager. This is made complex by the flexible SCSI Manager 4.3
  7.                 architecture: it is possible for the asynchronous SCSI Manager to only
  8.                  be available on a third-party bus interface, for example. Because of this,
  9.                 we must always scan the bus using the original SCSI Manager even if the
  10.                 asynchronous manager is present.
  11.  
  12.     Written by: Martin Minow.    
  13.  
  14.     Copyright:    Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
  15.  
  16.                 You may incorporate this Apple sample source code into your program(s) without
  17.                 restriction. This Apple sample source code has been provided "AS IS" and the
  18.                 responsibility for its operation is yours. You are not permitted to redistribute
  19.                 this Apple sample source code as "Apple sample source code" after having made
  20.                 changes. If you're going to re-distribute the source, we require that you make
  21.                 it clear in the source that the code was descended from Apple sample source
  22.                 code, but that you've made changes.
  23.  
  24.     Change History (most recent first):
  25.                 7/14/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  26.                 
  27.  
  28. */
  29. #include <Errors.h>
  30. #include <Events.h>
  31. #include <Memory.h>
  32. #include <OSUtils.h>
  33. #include <Traps.h>
  34. #include <Types.h>
  35. #include "SCSIFindDevices.h"
  36. #ifndef FALSE
  37. #define FALSE    0
  38. #define TRUE    1
  39. #endif
  40.  
  41. /*
  42.  * All functions use the same formal parameter to access the "global" record.
  43.  */
  44. #define REC    (*scsiFindDevicesPtr)
  45.  
  46. /*
  47.  * These are the states that control the overall process.
  48.  */
  49. enum {
  50.     kStateInitialize = 0,
  51.     kStateNextBus,
  52.     kStateNextTarget,
  53.     kStateNextLUN,
  54.     kStateCheckForHardWired,
  55.     kStateNextHardWiredTarget,
  56.     kStateNextHardWiredLUN,
  57.     kStateLastStateWithoutATrailingCommaBecauseWeCareAboutYou
  58. };
  59.  
  60. /*
  61.  * These are the commands that may be sent to the device. Request Sense is only
  62.  * sent by the original SCSI Manager.
  63.  */
  64. #define kScsiCmdInquiry                0x12
  65. #define kScsiCmdRequestSense        0x03
  66.  
  67. /*
  68.  * These are the device types that SCSI knows about.
  69.  */    
  70. enum {
  71.     kScsiDevTypeDirect                    = 0,
  72.     kScsiDevTypeSequential,
  73.     kScsiDevTypePrinter,
  74.     kScsiDevTypeProcessor,
  75.     kScsiDevTypeWorm,                        /* Write-once, read multiple        */
  76.     kScsiDevTypeCDROM,
  77.     kScsiDevTypeScanner,
  78.     kScsiDevTypeOptical,
  79.     kScsiDevTypeChanger,
  80.     kScsiDevTypeComm,
  81.     kScsiDevTypeGraphicArts0A,
  82.     kScsiDevTypeGraphicArts0B,
  83.     kScsiDevTypeFirstReserved,                /* Start of reserved sequence        */
  84.     kScsiDevTypeUnknownOrMissing        = 0x1F,
  85.     kScsiDevTypeMask                    = 0x1F
  86. };
  87. /*
  88.  * These are device type modifiers. We need them to distinguish between "unknown"
  89.  * and "missing" devices.
  90.  */
  91. enum {
  92.     kScsiDevTypeQualifierConnected        = 0x00,    /* Exists and is connected        */
  93.     kScsiDevTypeQualifierNotConnected    = 0x20,    /* Logical unit exists            */
  94.     kScsiDevTypeQualifierReserved        = 0x40,
  95.     kScsiDevTypeQualifierMissing        = 0x60,    /* No such logical unit            */
  96.     kScsiDevTypeQualifierVendorSpecific    = 0x80,    /* Other bits are unspecified    */
  97.     kScsiDevTypeQualifierMask            = 0xE0
  98. };
  99. #define kScsiDevTypeMissing    \
  100.     (kScsiDevTypeUnknownOrMissing | kScsiDevTypeQualifierMissing)
  101. /*
  102.  * This is the data that is returned after a GetExtendedStatus request. The
  103.  * errorCode gives a general indication of the error, which may be qualified by
  104.  * the additionalSenseCode and additionalSenseQualifier fields. These may be
  105.  * device (vendor) specific values, however. The info[] field contains additional
  106.  * information. For a media error, it contains the failing logical block number
  107.  * (most-significant byte first).
  108.  */
  109. struct SCSI_Sense_Data {                    /* Request Sense result                */
  110.     unsigned char        errorCode;            /*  0    Class code, valid lbn        */
  111.     unsigned char        segmentNumber;        /*  1    Segment number                */
  112.     unsigned char        senseKey;            /*  2    Sense key and flags            */
  113.     unsigned char        info[4];
  114.     unsigned char        additionalSenseLength;
  115.     unsigned char        reservedForCopy[4];
  116.     unsigned char        additionalSenseCode;
  117.     unsigned char        additionalSenseQualifier;    
  118.     unsigned char        fruCode;            /* Field replacable unit code        */
  119.     unsigned char        senseKeySpecific[2];
  120.     unsigned char        additional[101];
  121. };
  122. typedef struct SCSI_Sense_Data SCSI_Sense_Data;
  123. /*
  124.  * The high-bit of errorCode signals whether there is a logical
  125.  * block. The low value signals whether there is a valid sense
  126.  */
  127. #define kScsiSenseHasLBN            0x80    /* Logical block number set            */
  128. #define kScsiSenseInfoValid            0x70    /* Is sense key valid?                */
  129. #define kScsiSenseInfoMask            0x70    /* Mask for sense info                */
  130. /*
  131.  * These bits may be set in the sense key
  132.  */
  133. #define kScsiSenseKeyMask            0x0F
  134. #define kScsiSenseILI                0x20    /* Illegal logical Length            */
  135. #define kScsiSenseEOM                0x40    /* End of media                        */
  136. #define kScsiSenseFileMark            0x80    /* End of file mark                    */
  137.  
  138. /*
  139.  * SCSI sense codes. (Returned after request sense).
  140.  */
  141. #define     kScsiSenseNone                0x00    /* No error                            */
  142. #define     kScsiSenseRecoveredErr        0x01    /* Warning                            */
  143. #define     kScsiSenseNotReady            0x02    /* Device not ready                    */
  144. #define     kScsiSenseMediumErr        0x03    /* Device medium error                */
  145. #define     kScsiSenseHardwareErr        0x04    /* Device hardware error            */
  146. #define     kScsiSenseIllegalReq        0x05    /* Illegal request for dev.            */
  147. #define     kScsiSenseUnitAtn            0x06    /* Unit attention (not err)            */
  148. #define     kScsiSenseDataProtect        0x07    /* Data protection                    */
  149. #define     kScsiSenseBlankCheck        0x08    /* Tape-specific error                */
  150. #define     kScsiSenseVendorSpecific    0x09    /* Vendor-specific error            */
  151. #define     kScsiSenseCopyAborted        0x0a    /* Copy request cancelled            */
  152. #define     kScsiSenseAbortedCmd        0x0b    /* Initiator aborted cmd.            */
  153. #define     kScsiSenseEqual            0x0c    /* Comparison equal                    */
  154. #define     kScsiSenseVolumeOverflow    0x0d    /* Write past end mark                */
  155. #define     kScsiSenseMiscompare        0x0e    /* Comparison failed                */
  156. #define     kScsiSenseCurrentErr        0x70
  157. #define     kScsiSenseDeferredErr        0x71
  158.  
  159.  
  160. /*
  161.  * SCSI command status (from status phase)
  162.  */
  163. #define     kScsiStatusGood            0x00    /* Normal completion                */
  164. #define     kScsiStatusCheckCondition    0x02    /* Need GetExtendedStatus            */
  165. #define     kScsiStatusConditionMet    0x04    /* For Compare Command?                */
  166. #define     kScsiStatusBusy            0x08    /* Device busy (self-test?)            */
  167. #define     kScsiStatusIntermediate    0x10    /* Intermediate status                */
  168. #define     kScsiStatusResConflict        0x18    /* Reservation conflict                */
  169. #define     kScsiStatusQueueFull        0x28    /* Target can't do command            */
  170. #define     kScsiStatusReservedMask    0x3e    /* Vendor specific?                    */
  171.  
  172. /*
  173.  * This is the maximum number of times we try to grab the SCSI Bus
  174.  */
  175. #define kMaxSCSIRetries                40        /* 10 seconds, 4 times/sec            */
  176. /*
  177.  * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
  178.  * if either the BSY or SEL bit is set).
  179.  */
  180. #ifndef kScsiStatBSY
  181. #define kScsiStatBSY                (1 << 6)
  182. #endif
  183. #ifndef kScsiStatSEL
  184. #define kScsiStatSEL                (1 << 1)
  185. #endif
  186. #define ScsiBusBusy()        ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
  187.  
  188. /*
  189.  * These private routines do all the work.
  190.  */
  191. static void                    CheckForAsyncSCSI(
  192.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  193.     );
  194. static void                    GetHighHostBusAdaptor(
  195.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  196.     );
  197. static OSErr                SetupForNextSCSIBus(
  198.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  199.     );
  200. static Boolean                IsRegisteredAsynchDevice(
  201.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  202.     );
  203. static Boolean                CheckForDevice(
  204.         register SCSIFindDevicesPtr    scsiFindDevicesPtr,
  205.         Boolean                    useAsyncManager
  206.     );
  207. static OSErr                OriginalSCSI(
  208.         unsigned short            targetID,
  209.         Ptr                        command,
  210.         Ptr                        resultData,
  211.         unsigned long            resultSize,
  212.         long                    *actualTransferSize
  213.     );
  214. static void                    ClearMemory(
  215.         void                    *recordPtr,
  216.         unsigned long            recordLength
  217.     );
  218. #define CLEAR(record)        ClearMemory(&record, sizeof record);
  219.  
  220.  
  221. /*
  222.  * This is the function that does all the work. Each time it is called, it finds
  223.  * the next device (next logical unit, next target, next bus). The overall design
  224.  * uses a state machine that continues within the function until one of three
  225.  * things happen: it finds a device, it reaches the end of the device sequence,
  226.  * or it gets an error.
  227.  */
  228. OSErr
  229. SCSIFindNextDevice(
  230.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  231.     )
  232. {
  233.         OSErr                        status;
  234.         Boolean                        doNextState;
  235.         
  236.         if (REC.deviceID.bus == 0xFF)
  237.             REC.state = kStateInitialize;
  238.         /*
  239.          * This is the overall state machine that processes application calls.
  240.          * Code in this section only organizes the lower-level routines. Many of
  241.          * the tests within the state switch change state. In all cases, they set
  242.          * doNextState TRUE and exit the switch. This, eventually, continues at
  243.          * the while statement which re-runs the state switch. A state-machine
  244.          * organization is not necessarily the best (and goto's would be slightly
  245.          * more efficient), but looping through a common switch statement
  246.          * simplified debugging.
  247.          */
  248.         doNextState = TRUE;
  249.         while (doNextState) {
  250.             doNextState = FALSE;
  251.             switch (REC.state) {
  252.             case kStateInitialize:
  253.                 /*
  254.                  * Initialization: check for the presence of the asynchronous
  255.                  * SCSI Manager and get the last host bus. Then start with the
  256.                  * first bus.
  257.                  */
  258.                 REC.scsiExecIOPB = NULL;
  259.                 REC.execIOPBSize = 0;
  260.                 CheckForAsyncSCSI(scsiFindDevicesPtr);
  261.                 GetHighHostBusAdaptor(scsiFindDevicesPtr);
  262.                 REC.state = kStateNextBus;
  263.                 doNextState = TRUE;
  264.                 break;
  265.             case kStateNextBus:
  266.                 /*
  267.                  * Look for the first/next bus. If we have a bus, we do some
  268.                  * bus-specific initializations including, primarily, creating
  269.                  * the SCSI parameter block.
  270.                  */
  271.                 if (REC.deviceID.bus == 0xFF)
  272.                     REC.deviceID.bus = 0;            /* Do the first bus            */
  273.                 else {
  274.                     ++REC.deviceID.bus;                /* Do the next bus            */
  275.                 }
  276.                 if (REC.deviceID.bus <= REC.lastHostBus) {
  277.                     /*
  278.                      * We have another bus to test. Make a SCSI parameter block
  279.                      * that is properly sized for this bus.
  280.                      */
  281.                     status = SetupForNextSCSIBus(scsiFindDevicesPtr);
  282.                     if (status == noErr) {
  283.                         /*
  284.                          * This bus exists: check its targets.
  285.                          */
  286.                         REC.deviceID.targetID = 0xFF;
  287.                         REC.state = kStateNextTarget;
  288.                         doNextState = TRUE;
  289.                     }
  290.                     else if (status == eofErr) {    /* eofErr is private        */
  291.                         /*
  292.                          * This bus does not exist. There may be gaps in the bus
  293.                          * sequence if a third-party SCSI adaptor installs its
  294.                          * own, private, SCSI Manager 4.3 on a machine that does
  295.                          * not otherwise support asynchronous SCSI. Just continue
  296.                          * with the next bus until we reach REC.lastHostBus.
  297.                          */
  298.                         REC.state = kStateNextBus;
  299.                         doNextState = TRUE;
  300.                     }
  301.                     else {
  302.                         /*
  303.                          * Oops: this is a serious error. Since doNextState is
  304.                          * FALSE, we will exit the while loop and return to the
  305.                          * caller with a serious error.
  306.                          */
  307.                     }
  308.                 }
  309.                 else {
  310.                     /*
  311.                      * We've examined all of the buses accessable through SCSI
  312.                      * Manager 4.3. Check for third-party devices that are only
  313.                      * accessable through the original SCSI Manager (because they
  314.                      * patch the SCSI Manager traps).
  315.                      */
  316.                     REC.deviceID.targetID = 0xFF;
  317.                     REC.state = kStateCheckForHardWired;
  318.                     doNextState = TRUE;
  319.                 }
  320.                 break;
  321.             case kStateNextTarget:
  322.                 /*
  323.                  * Look for the next device on this bus. If we run off the end,
  324.                  * cycle back through the switch to look for the next bus.
  325.                  */
  326.                 if (REC.deviceID.targetID == 0xFF)
  327.                     REC.deviceID.targetID = 0;            /* Do the first target    */
  328.                 else {
  329.                     ++REC.deviceID.targetID;            /* Do the next target    */
  330.                 }
  331.                 /*
  332.                  * REC.initiatorID is set on a bus-by-bus basis. We cannot assume
  333.                  * that it is always equal to seven. Skip over the initiator.
  334.                  * REC.maxTargetID is normally seven; but it's provided to us
  335.                  * by the SCSI Manager, so we'll use that value.
  336.                  */ 
  337.                 if (REC.deviceID.targetID == REC.initiatorID)
  338.                     ++REC.deviceID.targetID;
  339.                 if (REC.deviceID.targetID > REC.maxTargetID) {
  340.                     /*
  341.                      * We've done all targets for this bus. Go on to the next bus.
  342.                      */
  343.                     REC.state = kStateNextBus;
  344.                 }
  345.                 else {
  346.                     /*
  347.                      * New target: do the first logical unit for this bus/target.
  348.                      */
  349.                     REC.deviceID.LUN = 0xFF;
  350.                     REC.state = kStateNextLUN;
  351.                 }
  352.                 doNextState = TRUE;
  353.                 break;
  354.             case kStateNextLUN:
  355.                 /*
  356.                  * We have a host bus and target ID. Cycle through the logical
  357.                  * units for this target ID. We will always look at LUN zero.
  358.                  * When we reach the end, the switch will take us to the next
  359.                  * target ID.
  360.                  */
  361.                 if (REC.deviceID.LUN == 0xFF)
  362.                     REC.deviceID.LUN = 0;                /* Do the first LUN        */
  363.                 else {
  364.                     ++REC.deviceID.LUN;                    /* Do the next LUN        */
  365.                 }
  366.                 if (REC.deviceID.LUN > REC.maxBusLUN) {
  367.                     REC.state = kStateNextTarget;
  368.                     doNextState = TRUE;
  369.                 }
  370.                 else {
  371.                     /*
  372.                      * Look for this LUN. Failures look for the next target.
  373.                      */
  374.                     if (CheckForDevice(scsiFindDevicesPtr, REC.useAsynchSCSI))
  375.                          status = noErr;                /* This one exists        */
  376.                     else {
  377.                         /*
  378.                          * This target/LUN was not found. Go on to the next
  379.                          * SCSI bus ID. Note that we presume that there are no
  380.                          * gaps in the LUN sequence.
  381.                          */
  382.                         REC.state = kStateNextTarget;
  383.                         doNextState = TRUE;
  384.                     }
  385.                 }
  386.                 break;
  387.             case kStateCheckForHardWired:
  388.                 /*
  389.                  * We have found all devices that can be accessed through the
  390.                  * asynchronous SCSI Manager. Some third-party hardware interfaces
  391.                  * do not use the asynchronous SCSI Manager, but patch original
  392.                  * SCSI traps. If the asynchronous SCSI Manager is not available,
  393.                  * we don't have to continue here (the above code found all
  394.                  * devices. In this segment, we must hard-wire the initiator ID
  395.                  * to seven, as there is no supported way to determine it from the
  396.                  * SCSI Manager or operating system.
  397.                  */
  398.                 if (REC.isAsyncSCSIPresent == FALSE)
  399.                     status = eofErr;                /* All done, thank you        */
  400.                 else {
  401.                     REC.deviceID.bus = 0;            /* Set the hard-wired data    */
  402.                     REC.initiatorID = 7;
  403.                     REC.maxTargetID = 7;
  404.                     REC.state = kStateNextHardWiredTarget;
  405.                     REC.deviceID.targetID = 0xFF;
  406.                     doNextState = TRUE;
  407.                 }
  408.                 break;
  409.             case kStateNextHardWiredTarget:
  410.                 /*
  411.                  * Look at the next target on the built-in bus.
  412.                  */
  413.                 if (REC.deviceID.targetID == 0xFF)
  414.                     REC.deviceID.targetID = 0;
  415.                 else {
  416.                     ++REC.deviceID.targetID;
  417.                 }
  418.                 if (REC.deviceID.targetID == REC.initiatorID)
  419.                     ++REC.deviceID.targetID;
  420.                 if (REC.deviceID.targetID >= REC.maxTargetID)
  421.                     status = eofErr;                /* All done, thank you        */
  422.                 else {
  423.                     REC.deviceID.LUN = 0xFF;
  424.                     REC.state = kStateNextHardWiredLUN;
  425.                     doNextState = TRUE;
  426.                 }
  427.                 break;
  428.             case kStateNextHardWiredLUN:
  429.                 /*
  430.                  * Look for the next LUN on the built-in bus.
  431.                  */
  432.                 if (REC.deviceID.LUN == 0xFF) {
  433.                     REC.maxBusLUN = REC.maxLUN;
  434.                     REC.deviceID.LUN = 0;                /* Do the first LUN        */
  435.                 }
  436.                 else {
  437.                     ++REC.deviceID.LUN;                    /* Do the next LUN        */
  438.                 }
  439.                 if (REC.deviceID.LUN > REC.maxBusLUN) {
  440.                     REC.state = kStateNextHardWiredTarget;
  441.                     doNextState = TRUE;
  442.                 }
  443.                 else {
  444.                     /*
  445.                      * Look for this LUN. failures look for the next target.
  446.                      * IsRegisteredAsynchDevice is TRUE if this device did not
  447.                      * register with the asynchronous SCSI Manager.
  448.                      */
  449.                     if (IsRegisteredAsynchDevice(scsiFindDevicesPtr)) {
  450.                         /*
  451.                          * Since we know about this device, it would have been
  452.                          * found by the first pass through the asynchronous
  453.                          * SCSI Manager. So, we don't need to look at it here.
  454.                          */
  455.                         REC.state = kStateNextHardWiredLUN;
  456.                         doNextState = TRUE;
  457.                     }
  458.                     else if (CheckForDevice(scsiFindDevicesPtr, FALSE)) {
  459.                         /*
  460.                          * This device was not registered, but it does exist
  461.                          * if we check using the original SCSI Manager. Probably
  462.                          * because someone patched the SCSI Manager traps.
  463.                          * Return this to the caller.
  464.                          */
  465.                          status = noErr;                /* This one exists        */
  466.                     }
  467.                     else {
  468.                         /*
  469.                          * This target/LUN was not found. Go on to the next
  470.                          * SCSI bus ID. Note that we presume that there are no
  471.                          * gaps in the LUN sequence.
  472.                          */
  473.                         REC.state = kStateNextHardWiredTarget;
  474.                         doNextState = TRUE;
  475.                     }
  476.                 }
  477.                 break;
  478.             default:                                    /* Illegal state        */
  479.                 status = abortErr;                        /* Can't happen            */
  480.                 break;
  481.             }                                            /* State switch            */
  482.         }                                                /* While doNextState    */
  483.         if (status != noErr) {
  484.             /*
  485.              * We've done everything that we can. Re-initialize and release
  486.              * our saved parameter block.
  487.              */
  488.             REC.deviceID.bus = 0xFF;                    /* Force reinitialize    */
  489.             if (REC.scsiExecIOPB != NULL) {
  490.                 DisposePtr((Ptr) REC.scsiExecIOPB);
  491.                 REC.scsiExecIOPB = NULL;
  492.             }
  493.         }
  494.         return (status);
  495. }
  496.  
  497. /*
  498.  * Check whether the Asynchronous SCSI trap is available.
  499.  * Set REC.isAsyncSCSI appropriately.
  500.  */
  501. static void
  502. CheckForAsyncSCSI(
  503.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  504.     )
  505. {
  506.         TrapType                trapType;
  507.         short                    theTrap;
  508.  
  509. /*
  510.  * TrapAvailable (see Inside Mac VI 3-8)
  511.  */
  512. #define NumToolboxTraps() (                                \
  513.         (NGetTrapAddress(_InitGraf, ToolTrap)            \
  514.                 == NGetTrapAddress(0xAA6E, ToolTrap))    \
  515.             ? 0x200 : 0x400                                \
  516.     )
  517. #define GetTrapType(theTrap) (                            \
  518.         (((theTrap) & 0x0800) != 0) ? ToolTrap : OSTrap    \
  519.     )
  520.         theTrap = _SCSIAtomic;
  521.         trapType = GetTrapType(theTrap);
  522.         if (trapType == ToolTrap) {
  523.             theTrap &= 0x07FF;
  524.             if (theTrap >= NumToolboxTraps())
  525.                 theTrap = _Unimplemented;
  526.         }
  527.         REC.isAsyncSCSIPresent = (
  528.                 NGetTrapAddress(theTrap, trapType)
  529.                 != NGetTrapAddress(_Unimplemented, ToolTrap)
  530.             );
  531. #undef NumToolboxTraps
  532. #undef GetTrapType
  533. }
  534.  
  535. /*
  536.  * If we have the asynchronous SCSI Manager, find out how many buses are present
  537.  * on this system. If we're limited to the original SCSI Manager, force a "single
  538.  * bus" scan, since the function that actually calls the SCSI Manager ignores the
  539.  * bus if the asynchronous manager is not present.
  540.  */
  541. static void
  542. GetHighHostBusAdaptor(
  543.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  544.     )
  545. {
  546.         OSErr                            status;
  547.         SCSIBusInquiryPB                busInquiryPB;
  548. #define PB                                (busInquiryPB)
  549.  
  550.         if (REC.isAsyncSCSIPresent == FALSE)
  551.             REC.lastHostBus = 0;
  552.         else {
  553.             CLEAR(PB);
  554.             PB.scsiPBLength = sizeof PB;
  555.             PB.scsiFunctionCode = SCSIBusInquiry;
  556.             PB.scsiDevice.bus = 0xFF;
  557.             status = SCSIAction((SCSI_PB *) &PB);
  558.             REC.lastHostBus = PB.scsiHiBusID;
  559.         }
  560. #undef PB
  561. }
  562.  
  563. /*
  564.  * Start to check a new SCSI bus. If we find a bus, allocate (or re-allocate)
  565.  * the SCSIExecIO command block. Note that it is possible to have buses with
  566.  * no devices, and gaps in the bus sequence. For example, in a Macintosh with
  567.  * two buses (such as the Quadra 950 or PowerMac 8100), you can have a bus
  568.  * with no devices.Also, if you install a third-party bus adaptor that supports
  569.  * the asynchronous SCSI Manager on a machine with two buses, it would be
  570.  * assigned bus 2 (with buses 0 and 1 referencing the internal system buses).
  571.  * In this case, a system could have no devices on bus 0 or 1.
  572.  */ 
  573. static OSErr
  574. SetupForNextSCSIBus(
  575.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  576.     )
  577. {
  578.         OSErr                        status;
  579.         SCSIBusInquiryPB            busInquiryPB;
  580. #define PB                            (busInquiryPB)
  581.  
  582.         /*
  583.          * If we don't support asynchronous SCSI, nothing happens here.
  584.          */
  585.         if (REC.isAsyncSCSIPresent == FALSE) {
  586.             REC.useAsynchSCSI = FALSE;
  587.             REC.initiatorID = 7;
  588.             REC.maxTargetID = 7;
  589.             REC.maxBusLUN = REC.maxLUN;
  590.             status = noErr;
  591.         }
  592.         else {
  593.             CLEAR(PB);
  594.             PB.scsiPBLength = sizeof PB;
  595.             PB.scsiFunctionCode = SCSIBusInquiry;
  596.             PB.scsiDevice.bus = REC.deviceID.bus;    /* Other values are zero    */
  597.             status = SCSIAction((SCSI_PB *) &PB);
  598.             if (status == noErr) {
  599.                 REC.useAsynchSCSI = TRUE;            /* Asynch works on this bus    */
  600.                 REC.initiatorID = PB.scsiInitiatorID;
  601.                 REC.maxTargetID = PB.scsiMaxTarget;
  602.                 /*
  603.                  * If we are running on a Quadra 840-AV with a CD300, the
  604.                  * Macintosh will hang if it tries to access LUN 1. We check for
  605.                  * this problem in two ways: by examining a global maxLUN flag,
  606.                  * and by checking whether the bug was fixed, either by running
  607.                  * on later hardware or by installing a System Update.
  608.                  */
  609.                 REC.enableATN =
  610.                     (busInquiryPB.scsiWeirdStuff & scsiTargetDrivenSDTRSafe) != 0; 
  611.                 REC.maxBusLUN = (REC.enableATN) ? REC.maxLUN : 0;
  612.                 /*
  613.                  * Allocate a parameter block for this request using the size that
  614.                  * was returned in the busInquiry parameter block.
  615.                  */
  616.                 if (REC.execIOPBSize != PB.scsiIOpbSize) {
  617.                     if (REC.scsiExecIOPB != NULL) {
  618.                         DisposePtr((Ptr) REC.scsiExecIOPB);
  619.                         REC.scsiExecIOPB = NULL;
  620.                     }
  621.                     REC.scsiExecIOPB =
  622.                                 (SCSIExecIOPB *) NewPtrClear(PB.scsiIOpbSize);
  623.                     if (REC.scsiExecIOPB == NULL)
  624.                         status = MemError();
  625.                     else {
  626.                         REC.execIOPBSize = PB.scsiIOpbSize;
  627.                         status = noErr;
  628.                     }
  629.                 }
  630.             }
  631.             else if (PB.scsiDevice.bus == 0) {
  632.                 /*
  633.                  * If bus zero is not registered, it must be accessed via the
  634.                  * original API. Set the initiatorID to the default 7 - there is
  635.                  * no supported mechanism for determining the actual id (it's
  636.                  * hidden inside the parameter RAM, but cannot be set by a
  637.                  * published mechanism).
  638.                  */
  639.                 REC.useAsynchSCSI = FALSE;
  640.                 REC.initiatorID = 7;
  641.                 REC.maxTargetID = 7;
  642.                 REC.maxBusLUN = REC.maxLUN;
  643.                 status = noErr;
  644.             }
  645.             else {
  646.                 /*
  647.                  * This is a problem: this bus cannot be accessed unless it has
  648.                  * been registered. For example, the second bus of a Quadra
  649.                  * 950 cannot be accessed if the SCSI Manager extension is not
  650.                  * installed and a third-party asynchronous SCSI Manager card
  651.                  * is installed as bus 2. Return a private error to skip this bus.
  652.                  */
  653.                 status = eofErr;
  654.             }
  655.         }
  656.         return (status);
  657. #undef PB
  658. }
  659.  
  660. /*
  661.  * This is called if the asynchronous SCSI Manager is present to sweep up any
  662.  * third party devices that did not register with the asynchronous SCSI Manager.
  663.  */
  664. static Boolean
  665. IsRegisteredAsynchDevice(
  666.         register SCSIFindDevicesPtr    scsiFindDevicesPtr
  667.     )
  668. {
  669.         OSErr                            status;
  670.         SCSIGetVirtualIDInfoPB            scsiGetVirtualIDInfo;
  671.  
  672.          CLEAR(scsiGetVirtualIDInfo);
  673.          scsiGetVirtualIDInfo.scsiPBLength = sizeof scsiGetVirtualIDInfo;
  674.          scsiGetVirtualIDInfo.scsiOldCallID = REC.deviceID.targetID;
  675.          status = SCSIAction((SCSI_PB *) &scsiGetVirtualIDInfo);
  676.          return (status == noErr);
  677. }
  678.  
  679. /*
  680.  * This is the only function that sends SCSI commands to a device. It will send a
  681.  * Device Inquiry and, if Check Condition is returned, issue Request Sense.
  682.  */
  683. static Boolean
  684. CheckForDevice(
  685.         register SCSIFindDevicesPtr    scsiFindDevicesPtr,
  686.         Boolean                    useAsyncManager
  687.     )
  688. {
  689.  
  690.         OSErr                    status;
  691.         OSErr                    requestSenseStatus;
  692.         SCSI_Sense_Data            senseData;
  693. #define SENSE    (senseData)
  694.         Boolean                    result;
  695.         /*
  696.          * For the old SCSI Manager only.
  697.          */
  698.         unsigned char            command[6];
  699.         long                    tempLong;
  700.         
  701.         if (useAsyncManager) {
  702. #define PB    (*REC.scsiExecIOPB)
  703.             /*
  704.              * Setup the parameter block for the user's request.
  705.              */
  706.             PB.scsiPBLength = REC.execIOPBSize;
  707.             PB.scsiFunctionCode = SCSIExecIO;
  708.             PB.scsiTimeout = 1000;
  709.             PB.scsiDevice = REC.deviceID;
  710.             PB.scsiCDBLength = 6;
  711.             PB.scsiCDB.cdbBytes[0] = kScsiCmdInquiry;
  712.             PB.scsiCDB.cdbBytes[4] = sizeof REC.inquiry;
  713.             /*
  714.              * Stuff the LUN into the command for SCSI-1 devices.
  715.              */
  716.             /* PB.scsiCDB.cdbBytes[1] &= ~0xE0; -- already zero */
  717.             PB.scsiCDB.cdbBytes[1] |= (REC.deviceID.LUN & 0x03) << 5;
  718.             /*
  719.              * Specify the transfer direction, if any, and setup the other SCSI
  720.              * operation flags. scsiSIMQNoFreeze prevents the SCSI Manager from
  721.              * blocking further operation if an error is detected.
  722.              */
  723.             PB.scsiFlags = scsiTransferPolled;
  724.             PB.scsiDataPtr = (unsigned char *) &REC.inquiry;
  725.             PB.scsiDataLength = sizeof REC.inquiry;
  726.             PB.scsiDataType = scsiDataBuffer;
  727.             PB.scsiSensePtr = (unsigned char *) &senseData;
  728.             PB.scsiSenseLength = sizeof senseData;
  729.             PB.scsiFlags = scsiSIMQNoFreeze | scsiDirectionIn | scsiDontDisconnect;
  730.             if (REC.enableATN == FALSE)
  731.                 PB.scsiIOFlags |= scsiDisableSelectWAtn;
  732.             status = SCSIAction((SCSI_PB *) &PB);
  733.             if (status == noErr)
  734.                 status = PB.scsiResult;
  735.             /*
  736.              * Note: scsiDataRunError is issued if our transfer request was larger
  737.              * or smaller than the actual transfer length. We need to examine the
  738.              * actual transfer sizes to see how to handle this error. This is
  739.              * not necessarily complete or correct. The intent here is to supress
  740.              * the transfer length error when executing Device Inquiry or other
  741.              * administrative commands with variable-length result blocks.
  742.              */
  743.             REC.actualInquirySize = PB.scsiDataLength - PB.scsiDataResidual;
  744.             if (status == scsiDataRunError                /* Over/underrun error    */
  745.              && REC.actualInquirySize > 0)                /* And its a short read    */
  746.                 status = noErr;                            /* If so, ignore error    */            
  747.             /*
  748.              * If the device issued Check Condition and the SCSI Manager was able
  749.              * to retrieve a Request Sense datum, change the error to our private
  750.              * "Check Condition" status.
  751.              */
  752.             if (status == scsiNonZeroStatus
  753.              && (PB.scsiResultFlags & scsiAutosenseValid) != 0) {
  754.                  status = statusErr;
  755.                  requestSenseStatus = noErr;
  756.             }
  757. #undef PB
  758.         }
  759.         else {
  760.             CLEAR(command);
  761.             command[0] = kScsiCmdInquiry;
  762.             command[4] = sizeof REC.inquiry;
  763.             /*
  764.              * Stuff the LUN into the command.
  765.              */
  766.             /* command[1] &= ~0xE0;    -- already zero */
  767.             command[1] |= (REC.deviceID.LUN & 0x03) << 5;
  768.             status = OriginalSCSI(
  769.                         REC.deviceID.targetID,
  770.                         (Ptr) &command,
  771.                         (Ptr) &REC.inquiry,
  772.                         sizeof REC.inquiry,
  773.                         &REC.actualInquirySize
  774.                     );
  775.             if (status == statusErr) {
  776.                 /*
  777.                  * Check condition
  778.                  */
  779.                 CLEAR(command);
  780.                 command[0] = kScsiCmdRequestSense;
  781.                 command[4] = sizeof senseData;
  782.                 /*
  783.                  * Stuff the LUN into the command.
  784.                  */
  785.                 /* command[1] &= ~0xE0;    -- already zero */
  786.                 command[1] |= (REC.deviceID.LUN & 0x03) << 5;
  787.                 requestSenseStatus = OriginalSCSI(
  788.                             REC.deviceID.targetID,
  789.                             (Ptr) &command,
  790.                             (Ptr) &senseData,
  791.                             sizeof senseData,
  792.                             &tempLong
  793.                         );
  794.                 if (status == noErr)
  795.                     status = statusErr;
  796.             }
  797.         }
  798.         /*
  799.          * Look at the result.
  800.          */
  801.         switch (status) {
  802.         case noErr:
  803.             if (REC.inquiry.devType == kScsiDevTypeMissing)
  804.                 result = FALSE;
  805.             else {
  806.                 result = TRUE;                    /* Normal successful return        */
  807.             }
  808.             break;
  809.         case statusErr:
  810.             /*
  811.              * The target returned Check Condition. We need to look at the sense
  812.              * data, if any, to distinguish between an "offline" but present device,
  813.              * and a non-existant logical unit. Note: some drives return Check
  814.              * Condition, and "no sense error" if we try to access an incorrect
  815.              * logical unit. This might reasonably be remapped as "illegal request.
  816.              */
  817.             if (requestSenseStatus != noErr
  818.              || (SENSE.errorCode & kScsiSenseInfoMask) != kScsiSenseInfoValid)
  819.                  result = FALSE;
  820.              else {
  821.                 switch (SENSE.senseKey & kScsiSenseKeyMask) {
  822.                 case kScsiSenseIllegalReq:
  823.                     result = FALSE;
  824.                     break;
  825.                 default:
  826.                     /*
  827.                      * Wierd: some drives seem to set the End of Media bit
  828.                      * in the sense key if an invalid LUN is selected.
  829.                      */
  830.                     if ((SENSE.senseKey & kScsiSenseEOM) != 0)
  831.                         result = FALSE;
  832.                     else {
  833.                         result = TRUE;
  834.                     }
  835.                     break;
  836.                 }
  837.             }
  838.             break;    
  839.         default:                            /* Other errors == no such device    */
  840.             result = FALSE;
  841.             break;
  842.         }
  843.         return (result);
  844. }
  845.  
  846. /*
  847.  * This is a very limited wrapper for the original SCSI Manager that can handle
  848.  * Device Inquiry and Request Sense (only).
  849.  */
  850. static OSErr
  851. OriginalSCSI(
  852.         unsigned short            targetID,
  853.         Ptr                        command,
  854.         Ptr                        resultData,
  855.         unsigned long            resultSize,
  856.         long                    *actualTransferSize
  857.     )
  858. {
  859.         OSErr                    status;
  860.         OSErr                    completionStatus;
  861.         short                    totalTries;            /* Get/Select retries        */
  862.         short                    getTries;            /* Get retries                */
  863.         short                    iCount;                /* Bus free counter            */
  864.         unsigned long            watchdog;            /* Timeout after this        */
  865.         SCSIInstr                tib[4];
  866.         short                    messageByte;
  867.         short                    statusByte;
  868.  
  869.         *actualTransferSize = 0;
  870.         tib[0].scOpcode = scInc;
  871.         tib[0].scParam1 = (unsigned long) resultData;
  872.         tib[0].scParam2 = 1;
  873.         tib[1].scOpcode = scAdd;
  874.         tib[1].scParam1 = (unsigned long) actualTransferSize;
  875.         tib[1].scParam2 = 1;
  876.         tib[2].scOpcode = scLoop;
  877.         tib[2].scParam1 = (-2 * sizeof (SCSIInstr));
  878.         tib[2].scParam2 = resultSize;
  879.         tib[3].scOpcode = scStop;
  880.         tib[3].scParam1 = 0;
  881.         tib[3].scParam2 = 0;
  882.         /*
  883.          * Arbitrate for the scsi bus.  This will fail if some other device is
  884.          * accessing the bus at this time (which is unlikely).
  885.          *
  886.          *** Do not set breakpoints or call any functions that may require device
  887.          *** I/O (such as display code that accesses font resources between
  888.          *** SCSIGet and SCSIComplete,
  889.          *
  890.          */
  891.         for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
  892.             for (getTries = 0; getTries < 4; getTries++) {
  893.                 /*
  894.                  * Wait for the bus to go free.
  895.                  */
  896.                 watchdog = TickCount() + 300;        /* 5 second timeout            */
  897.                 while (ScsiBusBusy()) {
  898.                     if (TickCount() > watchdog) {
  899.                         status = scArbNBErr;
  900.                         goto exit;
  901.                     }
  902.                 }
  903.                 /*
  904.                  * The bus is free, try to grab it
  905.                  */
  906.                 for (iCount = 0; iCount < 4; iCount++) {
  907.                     if ((status = SCSIGet()) == noErr)
  908.                         break;
  909.                 }
  910.                 if (status == noErr)
  911.                     break;                            /* Success: we have the bus */
  912.                 /*
  913.                  * The bus became busy again. Try to wait for it to go free.
  914.                  */
  915.                 for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
  916.                     ;
  917.             } /* The getTries loop */
  918.             if (status != noErr) {
  919.                 /*
  920.                  * The SCSI Manager thinks the bus is not busy and not selected,
  921.                  * but "someone" has set its internal semaphore that signals
  922.                  * that the SCSI Manager itself is busy. The application will have
  923.                  * to handle this problem. (We tried getTries * 4 times).
  924.                  */
  925.                 goto exit;
  926.             }
  927.             /*
  928.              * We now own the SCSI bus. Try to select the device.
  929.              */
  930.             if ((status = SCSISelect(targetID)) != noErr)
  931.                 goto exit;
  932.             /*
  933.              * From this point on, we must exit through SCSIComplete() even if an
  934.              * error is detected. Send a command to the selected device. There are
  935.              * several failure modes, including an illegal command (such as a
  936.              * write to a read-only device). If the command failed because of
  937.              * "device busy", we will try it again.
  938.              */
  939.             status = SCSICmd((Ptr) command, 6);
  940.             if (status == noErr)
  941.                 status = SCSIRead((Ptr) tib);
  942.             /*
  943.              * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
  944.              * returning the status and command-completion message bytes..
  945.              */
  946.             completionStatus = SCSIComplete(&statusByte, &messageByte, 60L);
  947.             /*
  948.              * If we have an error here, return as the "final" status.
  949.              * 
  950.              */
  951.             if (completionStatus != noErr)
  952.                 status = completionStatus;
  953.             else {
  954.                 /*
  955.                  * ScsiComplete is happy. If the device is busy, Pause for 1/4
  956.                  * second and try again.
  957.                  */
  958.                 if (statusByte == kScsiStatusBusy) {
  959.                     watchdog = TickCount() + 15;
  960.                     while (TickCount() < watchdog)
  961.                         ;
  962.                     continue;                /* Do next totalTries attempt        */
  963.                 }
  964.             }
  965.             /*
  966.              * This is the normal exit (success) or final failure exit.
  967.              */
  968.             break;
  969.         } /* totalTries loop */
  970. exit:    /*
  971.          * Ignore phase errors if the buffer was large enough
  972.          */
  973.         if (status == scPhaseErr && *actualTransferSize <= resultSize)
  974.             status = noErr;
  975.         /*
  976.          * Return statusErr if the device returns Check Condition:
  977.          * Also, there is a bug in the combination of System 7.0.1 and the 53C96
  978.          * that may cause the real SCSI Status Byte to be in the Message byte.
  979.          */
  980.         if (statusByte == kScsiStatusGood
  981.          && messageByte == kScsiStatusCheckCondition)
  982.             statusByte = kScsiStatusCheckCondition;
  983.         if (status == noErr && statusByte == kScsiStatusCheckCondition)
  984.             status = statusErr;
  985.         return (status);
  986. }
  987.  
  988.  
  989. static void
  990. ClearMemory(
  991.         void                    *recordPtr,
  992.         register unsigned long    recordLength
  993.     )
  994. {
  995.         register char            *ptr;
  996.         
  997.         for (ptr = (char *) recordPtr; recordLength > 0; --recordLength)
  998.             *ptr++ = 0; 
  999. }
  1000.  
  1001.